package com.scorpio.sdk.service.impl;


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.scorpio.common.core.page.PageBean;
import com.scorpio.common.exception.CustomException;
import com.scorpio.common.utils.StringUtils;
import com.scorpio.common.utils.http.HttpUtils;
import com.scorpio.common.utils.http.Request;
import com.scorpio.redis.RedisCache;
import com.scorpio.redis.cachelock.RedisLock;
import com.scorpio.redis.constant.AppCacheConstant;
import com.scorpio.sdk.constant.WeiXinConstant;
import com.scorpio.sdk.dto.WxUserDTO;
import com.scorpio.sdk.service.WeiXinService;
import com.scorpio.system.service.ISysConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class WeiXinServiceImpl implements WeiXinService {
    @Autowired
    private ISysConfigService sysConfigService;

    @Autowired
    private RedisCache redisCache;


    @Autowired
    private RedisLock redisLock;

    @Override
    public String getWxMiniOpenId(String code) {
        String appid = sysConfigService.selectConfigByKey(WeiXinConstant.MINI_APP_ID);
        String secret = sysConfigService.selectConfigByKey(WeiXinConstant.MINI_SECRET);
        Map<String, String> params = new HashMap<>();
        params.put("js_code", code);
        params.put("appid", appid);
        params.put("secret", secret);
        params.put("grant_type", "authorization_code");
        String data = HttpUtils.request(
                new Request()
                        .get()
                        .url(WeiXinConstant.MINI_LOGIN_URL)
                        .params(params)
        );
        JSONObject body = JSONObject.parseObject(data);
        String openid = body.getString("openid");
        if (StringUtils.isEmpty(openid)) {
            throw new CustomException(body.getString("errmsg"));
        } else {
            return openid;
        }
    }

    @Override
    public String getAccessToken() {
        String accessToken = redisCache.getCacheObject(AppCacheConstant.CACHE_WEIXIN_ACCESS_TOKEN);
        if (StringUtils.isEmpty(accessToken)) {
            String key = "WeiXinServiceImpl_getAccessToken";
            if (redisLock.lock(key, 5)) {
                try {
                    String url = WeiXinConstant.WEIXIN_TENCENT_TOKEN;
                    String appid = sysConfigService.selectConfigByKey(WeiXinConstant.WEIXIN_TENCENT_APPID);
                    String secret = sysConfigService.selectConfigByKey(WeiXinConstant.WEIXIN_TENCENT_SECRET);
                    Map<String, String> params = new HashMap<>();
                    params.put("appid", appid);
                    params.put("secret", secret);
                    params.put("grant_type", "client_credential");
                    String data = HttpUtils.request(
                            new Request()
                                    .get()
                                    .url(url)
                                    .params(params)
                    );
                    JSONObject body = JSONObject.parseObject(data);
                    accessToken = body.getString("access_token");
                    if (!StringUtils.isEmpty(accessToken)) {
                        long expires_in = body.getLong("expires_in");
                        redisCache.setCacheObject(AppCacheConstant.CACHE_WEIXIN_ACCESS_TOKEN, accessToken, expires_in, TimeUnit.SECONDS);
                    } else {
                        WeiXinConstant.disposeResult(url, (JSONObject) JSONObject.toJSON(params), data, false);
                    }
                } finally {
                    redisLock.unlock(key);
                }
            }
        }
        return accessToken;
    }

    @Override
    public String getJsapiTicket() {
        String accessToken = getAccessToken();
        if (StringUtils.isEmpty(accessToken)) {
            throw new CustomException("请稍后再试");
        }
        String ticket = redisCache.getCacheObject(AppCacheConstant.CACHE_WEIXIN_JSAPI_TICKET);
        String key = "WeiXinServiceImpl_getJsapiTicket";
        if (redisLock.lock(key, 5)) {
            try {
                String url = WeiXinConstant.WEIXIN_TENCENT_GETTICKET;
                Map<String, String> params = new HashMap<>();
                params.put("access_token", accessToken);
                params.put("type", "jsapi");
                String data = HttpUtils.request(
                        new Request()
                                .get()
                                .url(url)
                                .params(params)
                );
                JSONObject body = JSONObject.parseObject(data);
                String code = body.getString("errcode");
                if ("0".equals(code)) {
                    ticket = body.getString("ticket");
                    long expires_in = body.getLong("expires_in");
                    redisCache.setCacheObject(AppCacheConstant.CACHE_WEIXIN_JSAPI_TICKET, ticket, expires_in, TimeUnit.SECONDS);
                } else {
                    WeiXinConstant.disposeResult(url, (JSONObject) JSONObject.toJSON(params), data, false);
                }
            } finally {
                redisLock.unlock(key);
            }
        }
        return ticket;
    }

    @Override
    public Map getSigntrue(String url) throws NoSuchAlgorithmException {
        String appid = sysConfigService.selectConfigByKey(WeiXinConstant.WEIXIN_TENCENT_APPID);

        String jsapi_ticket = getJsapiTicket();
        String noncestr = UUID.randomUUID().toString();
        String timestamp = "" + (System.currentTimeMillis() / 1000);
        //将四个数据进行组合，传给SHA1进行加密
        String str = "jsapi_ticket=" + jsapi_ticket +
                "&noncestr=" + noncestr +
                "&timestamp=" + timestamp +
                "&url=" + url;


        MessageDigest digest = MessageDigest.getInstance("SHA-1");
        digest.reset();
        digest.update(str.getBytes());
        byte messageDigest[] = digest.digest();
        StringBuffer hexStr = new StringBuffer();
        for (int i = 0; i < messageDigest.length; i++) {
            String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
            if (shaHex.length() < 2) {
                hexStr.append(0);
            }
            hexStr.append(shaHex);
        }

        Map<String, String> map = new HashMap<>();
        map.put("jsapi_ticket", jsapi_ticket);
        map.put("noncestr", noncestr);
        map.put("timestamp", timestamp);
        map.put("url", url);
        map.put("appid", appid);
        map.put("sign", hexStr.toString());
        map.remove("jsapi_ticket");
        return map;
    }


    @Override
    public void sendMsgToWeixin(String templateId, String toUserOpenId, JSONObject data) {
        JSONObject json = new JSONObject();
        json.put("touser", toUserOpenId);
        json.put("template_id", templateId);
        json.put("data", data);

        Map<String, String> params = new HashMap<>();
        params.put("access_token", getAccessToken());

        String bodyStr = HttpUtils.request(
                new Request()
                        .post()
                        .url(WeiXinConstant.WEIXIN_SEND_MSG_URL)
                        .params(params)
                        .body(json.toJSONString())
        );
        JSONObject body = JSONObject.parseObject(bodyStr);
        String errcode = body.getString("errcode");
        String errmsg = body.getString("errmsg");
        if (!"0".equals(errcode)) {
            throw new CustomException("消息发送失败-errcode=" + errcode + ";errmsg=" + errmsg);
        }
    }

    @Override
    public List<WxUserDTO> findUserList(String nextOpenid) {
        Map<String, String> params = new HashMap<>();
        params.put("access_token", getAccessToken());
        if (!StringUtils.isEmpty(nextOpenid)) params.put("next_openid", nextOpenid);
        String bodyStr = HttpUtils.request(
                new Request()
                        .get()
                        .url(WeiXinConstant.WEIXIN_USER_LIST_URL)
                        .params(params)
        );

        JSONObject body = JSONObject.parseObject(bodyStr);
        Integer errcode = body.getInteger("errcode");
        String errmsg = body.getString("errmsg");
        if (errcode != null) {
            throw new CustomException("消息发送失败-errcode=" + errcode + ";errmsg=" + errmsg);
        }
        JSONObject data = body.getJSONObject("data");
        JSONArray openid = data.getJSONArray("openid");
        String next_openid = body.getString("next_openid");
        long total = body.getLongValue("total");
        long count = body.getLongValue("count");


        PageBean pageBean = new PageBean();
        pageBean.setPage();

        int offset = pageBean.getOffset();
        int limit = pageBean.getLimit();
        offset = offset % 10000;


        JSONArray user_list = new JSONArray();

        for (int i = offset; i < offset + limit; i++) {
            if (i >= openid.size()) continue;
            String uid = openid.getString(i);
            if (!StringUtils.isEmpty(uid)) {
                JSONObject user = new JSONObject();
                user.put("openid", uid);
                user.put("lang", "zh_CN");
                user_list.add(user);
            }
        }

        params = new HashMap<>();
        params.put("access_token", getAccessToken());

        JSONObject requestData = new JSONObject();
        requestData.put("user_list", user_list);

        bodyStr = HttpUtils.request(
                new Request()
                        .post()
                        .url(WeiXinConstant.WEIXIN_USER_DETAILS_URL)
                        .body(requestData.toJSONString())
                        .params(params)
        );
        body = JSONObject.parseObject(bodyStr);
        errcode = body.getInteger("errcode");
        errmsg = body.getString("errmsg");
        if (errcode != null) {
            throw new CustomException("消息发送失败-errcode=" + errcode + ";errmsg=" + errmsg);
        }
        ArrayList<WxUserDTO> list = body.getObject("user_info_list", new TypeReference<ArrayList<WxUserDTO>>() {
        });
        return list;
    }
}
